"""
枚举 | 基类
"""

import enum

__all__ = [
    "StringEnum",  # ->     枚举基类（字符串）
    "IntegerEnum",  # ->    枚举基类（整数）
]

from my_typing import *

_EnumerationT = TypeVar("_EnumerationT", bound=type[enum.Enum])


class _ChoicesMeta(enum.EnumMeta):
    """
    用于创建 enum choices 的元类

    References:
        :class:`django.db.models.enums.ChoicesMeta`
    """

    name_type: Final[type[str]] = str
    """ name 的类型 """

    label_type: Final[type[str]] = str
    """ label 的类型 """

    def __new__(
        mcs: type[_EnumerationT],
        class_name: str,
        bases: tuple[type, ...],
        class_dict: enum._EnumDict,  # noqa: ignore[attribute]
    ) -> _EnumerationT:
        """
        创建一个枚举类

        Args:
            class_name: 类名
            bases: 类的基本属性
            class_dict: 类的字典信息

        Returns:
            枚举类

        Raises:
            AssertionError 1: 请注意格式: name = value, label
            AssertionError 2: label 只能为 str 类型
            AssertionError 3: value 只能为 int/str 类型
        """

        labels: ListStr = []
        """ 标签 """

        for _name in getattr(class_dict, "_member_names", []):
            raw_value: tuple[int | str, str] = class_dict[_name]
            """ 原始 value: value = (value, label)  """

            if len(raw_value) != 2:
                raise ValueError("请注意格式: name = value, label")

            _value, _label = raw_value

            if not isinstance(_label, str):
                raise TypeError("label 只能为 str 类型")

            if not isinstance(_value, (int, str)):
                raise TypeError("value 只能为 int/str 类型")

            value: tuple[int | str] = (_value,)

            labels.append(_label)

            dict.__setitem__(class_dict, _name, value)

        cls = super().__new__(mcs, class_name, bases, class_dict)
        cls._value2label_map_ = dict(zip(cls._value2member_map_, labels))
        cls.label = property(lambda self: cls._value2label_map_.get(self.value))
        cls.do_not_call_in_templates = True

        return enum.unique(cls)

    def __contains__(cls, member) -> bool:
        """
        判断某个值是否在该枚举类中定义

        Args:
            member: 要判断的成员对象

        Returns:
            True / False
        """

        if not isinstance(member, enum.Enum):
            return member in cls.values

        return super().__contains__(member)

    @property
    def names(cls) -> TupleStr:
        """ 返回所有 Name """

        return tuple(
            member.name
            for member in cls
        )

    @property
    def labels(cls) -> TupleStr:
        """ 返回所有 Label """

        return tuple(
            member.label
            for member in cls
        )

    @property
    def values(cls) -> tuple[int | str, ...]:
        """ 返回所有 Value """

        return tuple(
            member.value
            for member in cls
        )

    @property
    def choices(cls) -> tuple[tuple[int | str, str], ...]:
        """ 返回元组，内部元素是元组 (Value, Label) """

        return tuple(
            (member.value, member.label)
            for member in cls
        )

    @property
    def value_2_label(cls) -> dict[int | str, str]:
        """ 返回 Value-Label 映射 """

        return {
            member.value: member.label
            for member in cls
        }

    @property
    def label_2_value(cls) -> dict[str, int | str]:
        """ 返回 Label-Value 映射 """

        return {
            member.label: member.value
            for member in cls
        }

    @property
    def name_2_value(cls) -> dict[str, int | str]:
        """ 返回 Name-Value 映射 """

        return {
            member.name: member.value
            for member in cls
        }

    @property
    def value_2_name(cls) -> dict[int | str, str]:
        """ 返回 Value-Name 映射 """

        return {
            member.value: member.name
            for member in cls
        }

    @property
    def name_2_label(cls) -> DictStrStr:
        """ 返回 Name-Label 映射 """

        return {
            member.name: member.label
            for member in cls
        }

    @property
    def label_2_name(cls) -> DictStrStr:
        """ 返回 Label-Name 映射 """

        return {
            member.label: member.name
            for member in cls
        }


class _StringMeta(_ChoicesMeta):
    """ 字符串 枚举元类 """

    value_type: type[str] = str
    """ value 的类型 (str) """


class _IntegerMeta(_ChoicesMeta):
    """ 整数 枚举元类 """

    value_type: type[int] = int
    """ value 的类型 (int) """


class _ChoiceBase(enum.Enum):
    """ Choice 基类 """

    @property
    def name(self) -> str:
        """ Name """

        return self._name_

    @property
    def label(self) -> str:
        """ Label """

        return self.name_2_label[self._name_]  # noqa: ignore[attribute]

    def __str__(self) -> str:
        return str(self.value)


class IntegerEnum(
    int,
    _ChoiceBase,
    metaclass=_IntegerMeta,
):
    """ 枚举基类 | 整数 """

    @property
    def value(self) -> int:
        """ Value """

        return self._value_


class StringEnum(
    str,
    _ChoiceBase,
    metaclass=_StringMeta,
):
    """ 枚举基类 | 字符串 """

    @property
    def value(self) -> str:
        """ Value """

        return self._value_

    def _generate_next_value_(
        self,
        start: int,
        count: int,
        last_values: list,
    ) -> "StringEnum":
        """
        生成枚举类的下一个值

        在定义枚举类时用 `@enum.auto` 装饰器时自动生成的方法，用于自动计算枚举值。

        Args:
            start: 枚举值的起始值（一般默认为 1）
            count: 已经定义的枚举值的个数
            last_values: 已经定义的枚举值的列表

        Returns:
            枚举类本身
        """

        return self


'''
# -------------------- IntegerEnum 用法 --------------------

class Gender(IntegerEnum):
    """ 性别 枚举类 """

    UNKNOWN = 0, "未知"
    MALE = 1, "男"
    FEMALE = 2, "女"
    SECRET = 3, "保密"


print(Gender.UNKNOWN.name)    # "UNKNOWN"
print(Gender.UNKNOWN.value)   # 0
print(Gender.UNKNOWN.label)   # "未知"
print(Gender.values)          # (0, 1, 2, 3)
print(Gender.labels)          # ("未知", "男", "女", "保密")
print(Gender.names)           # ("UNKNOWN", "MALE", "FEMALE", "SECRET")
print(Gender.choices)         # ((0, "未知"), (1, "男"), (2, "女"), (3, "保密"))
print(Gender.value_2_label)   # {0: "未知", 1: "男", 2: "女", 3: "保密"}
print(Gender.label_2_value)   # {"未知": 0, "男": 1, "女": 2, "保密": 3}
print(Gender.name_2_value)    # {"UNKNOWN": 0, "MALE": 1, "FEMALE": 2, "SECRET": 3}
print(Gender.value_2_name)    # {0: "UNKNOWN", 1: "MALE", 2: "FEMALE", 3: "SECRET"}
print(Gender.name_2_label)    # {"UNKNOWN": "未知", "MALE": "男", "FEMALE": "女", "SECRET": "保密"}
print(Gender.label_2_name)    # {"未知": "UNKNOWN", "男": "MALE", "女": "FEMALE", "保密": "SECRET"}



# -------------------- StringEnum 用法 --------------------

class Direction(StringEnum):
    """ 方向 枚举类 """

    UP = "up", "上"
    DOWN = "down", "下"
    LEFT = "left", "左"
    RIGHT = "right", "右"


print(Direction.UP.name)         # "UP"
print(Direction.UP.value)        # "up"
print(Direction.UP.label)        # "上"
print(Direction.values)          # ("up", "down", "left", "right")
print(Direction.labels)          # ("上", "下", "左", "右")
print(Direction.names)           # ("UP", "DOWN", "LEFT", "RIGHT")
print(Direction.choices)         # (("up", "上"), ("down", "下"), ("left", "左"), ("right", "右"))
print(Direction.value_2_label)   # {"up": "上", "down": "下", "left": "左", "right": "右"}
print(Direction.label_2_value)   # {"上": "up", "下": "down", "左": "left", "右": "right"}
print(Direction.name_2_value)    # {"UP": "up", "DOWN": "down", "LEFT": "left", "RIGHT": "right"}
print(Direction.value_2_name)    # {"up": "UP", "down": "DOWN", "left": "LEFT", "right": "RIGHT"}
print(Direction.name_2_label)    # {"UP": "上", "DOWN": "下", "LEFT": "左", "RIGHT": "右"}
print(Direction.label_2_name)    # {"上": "UP", "下": "DOWN", "左": "LEFT", "右": "RIGHT"}



# -------------------- model 中的用法 --------------------

class UserProfile(models.Model):
    """ 用户信息 """

    class Meta:
        db_table = "user_userprofile"
        verbose_name = verbose_name_plural = "用户"
        ordering = ["-id"]

    class Gender(IntegerEnum):
        """ 性别 枚举类 """

        UNKNOWN = 0, "未知"
        MALE = 1, "男"
        FEMALE = 2, "女"
        SECRET = 3, "保密"

    id = models.AutoField(verbose_name="id", help_text="主键", primary_key=True)
    nickname = models.AutoField(verbose_name="昵称", unique=True)
    gender = models.SmallIntegerField(verbose_name="性别",choices=Gender.choices, default=Gender.UNKNOWN.value)
'''

if __name__ == "__main__":
    class Direction(StringEnum):
        """ 方向 枚举类 """

        UP = "up", "上"
        DOWN = "down", "下"
        LEFT = "left", "左"
        RIGHT = "right", "右"

    print(Direction.UP.name)  # "UP"
    print(Direction.UP.value)  # "up"
    print(Direction.UP.label)  # "上"
    print(Direction.values)  # ("up", "down", "left", "right")
    print(Direction.labels)  # ("上", "下", "左", "右")
    print(Direction.names)  # ("UP", "DOWN", "LEFT", "RIGHT")
    print(Direction.choices)  # (("up", "上"), ("down", "下"), ("left", "左"), ("right", "右"))
    print(Direction.value_2_label)  # {"up": "上", "down": "下", "left": "左", "right": "右"}
    print(Direction.label_2_value)  # {"上": "up", "下": "down", "左": "left", "右": "right"}
    print(Direction.name_2_value)  # {"UP": "up", "DOWN": "down", "LEFT": "left", "RIGHT": "right"}
    print(Direction.value_2_name)  # {"up": "UP", "down": "DOWN", "left": "LEFT", "right": "RIGHT"}
    print(Direction.name_2_label)  # {"UP": "上", "DOWN": "下", "LEFT": "左", "RIGHT": "右"}
    print(Direction.label_2_name)  # {"上": "UP", "下": "DOWN", "左": "LEFT", "右": "RIGHT"}
    print(Direction.name_type)  # <class 'str'>
    print(Direction.value_type)  # <class 'str'>
    print(Direction.label_type)  # <class 'str'>

    class Gender(IntegerEnum):
        """ 性别 枚举类 """

        UNKNOWN = 0, "未知"
        MALE = 1, "男"
        FEMALE = 2, "女"
        SECRET = 3, "保密"

    print(Gender.UNKNOWN.name)  # "UNKNOWN"
    print(Gender.UNKNOWN.value)  # 0
    print(Gender.UNKNOWN.label)  # "未知"
    print(Gender.values)  # (0, 1, 2, 3)
    print(Gender.labels)  # ("未知", "男", "女", "保密")
    print(Gender.names)  # ("UNKNOWN", "MALE", "FEMALE", "SECRET")
    print(Gender.choices)  # ((0, "未知"), (1, "男"), (2, "女"), (3, "保密"))
    print(Gender.value_2_label)  # {0: "未知", 1: "男", 2: "女", 3: "保密"}
    print(Gender.label_2_value)  # {"未知": 0, "男": 1, "女": 2, "保密": 3}
    print(Gender.name_2_value)  # {"UNKNOWN": 0, "MALE": 1, "FEMALE": 2, "SECRET": 3}
    print(Gender.value_2_name)  # {0: "UNKNOWN", 1: "MALE", 2: "FEMALE", 3: "SECRET"}
    print(Gender.name_2_label)  # {"UNKNOWN": "未知", "MALE": "男", "FEMALE": "女", "SECRET": "保密"}
    print(Gender.label_2_name)  # {"未知": "UNKNOWN", "男": "MALE", "女": "FEMALE", "保密": "SECRET"}
    print(Gender.label_2_name)  # {"未知": "UNKNOWN", "男": "MALE", "女": "FEMALE", "保密": "SECRET"}
    print(Gender.name_type)  # <class 'str'>
    print(Gender.value_type)  # <class 'int'>
    print(Gender.label_type)  # <class 'str'>
